﻿using System;
using System.Linq;
using System.Windows.Forms;
using System.IO;
using InformationRetrieval.Tokenizer;
using InformationRetrieval.Normalization;
using InformationRetrieval.Wildcard;
using InformationRetrieval.Indexer;
using System.Diagnostics;

namespace InformationRetrieval
{
    /// <summary>
    /// Demonstrates how an InvertedIndex can be used in a full-text search.
    /// </summary>
    public partial class SearchForm : Form
    {
        #region Fields
        INormalizer _normalizer = new DummyNormalizer();
        IInvertedIndex _invertedIndex = null;
        IWildcardIndex _wildcardIndex = null;
        #endregion

        public SearchForm()
        {
            InitializeComponent();
        }

        #region EventHandlers
        private void uxSearch_Click(object sender, EventArgs e)
        {
            Search();
        }

        private void uxQuery_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Return)
            {
                Search();
            }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Build the InvertedIndex. All .txt-Files in the folder are added to the index.
        /// </summary>
        private void BuildIndex()
        {
            _wildcardIndex = new KGramIndex();
            _normalizer = new PorterStemmer();
            _invertedIndex = new InvertedIndex(_wildcardIndex, _normalizer);
            ITokenizer tokenizer = new EnglishTokenizer();

            Stopwatch watch = new Stopwatch();
            watch.Start();	
			
			//This path really should be made dynamic, but I don't have the time to do this now
            foreach (var s in Directory.EnumerateFiles(@"D:\FH-Salzburg\Semester\9-10\IIR - Entacher, Kranzer\IR_Project\gutenberg", "*.txt").Take(10))
            {
                FileInfo doc = new FileInfo(s);
                string docName = doc.FullName;
                using (TextReader reader = new StreamReader(s))
                {
					// Well, pretty obvious here, add all documents to the index
                    _invertedIndex.AddDocument(docName, tokenizer.Tokenize(reader, true));
                }
            }
            watch.Stop();

            //_invertedIndex.Persist(); FIXME: Seems there is a problem here, we get double results. Also: slooowwwww....
        }

		/// <summary>
        /// Starts the search query and displays the results. If the InvertedIndex is not yet built,
        /// build it.
		/// </summary>
        private void Search()
        {
            try
            {
                //clear currently displayed items
                resultList.Clear();
                tbInfo.Text = "";

                Stopwatch watch = new Stopwatch();
                watch.Start();

                if (_invertedIndex == null) BuildIndex();

				// This one actually runs the search
                var results = _invertedIndex.Find(uxQuery.Text);

                if (!results.Any()) // If no results are found
                {
                    ListViewGroup newGroup = new ListViewGroup("Not found!");//
                    resultList.Groups.Add(newGroup);
                    resultList.Items.Add(new ListViewItem("Buy \"Not found!\" at Amazon!", newGroup));
                    resultList.Items.Add(new ListViewItem("Ebay: \"Not found!\" for just 9.95$!", newGroup));
                    resultList.Items.Add(new ListViewItem("Enlarge your \"Not found!\" to 8 inches NOW!", newGroup));
                }

                // Display each result in the list
                foreach (var result in results)
                {
                    ListViewGroup newGroup = new ListViewGroup(Path.GetFileName(result.Key.ToString()));
                    resultList.Groups.Add(newGroup);

                    // Read the document and retrieve text from positions
                    var fileContents = File.ReadAllText(result.Key);

                    //die position gibt an, bei dem wievielten byte in der datei das wort beginnt
                    foreach (int position in result.Value.TermPositions)
                    {
                        int startIndex = System.Convert.ToInt32(position) - 21;
                        startIndex = startIndex < 0 ? 0 : startIndex;

                        int length = 42;
                        if (startIndex + length > fileContents.Length - 1)
                            length = fileContents.Length - startIndex - 1;

                        resultList.Items.Add(new ListViewItem("..."+fileContents.Substring(startIndex, length)+"...", newGroup));
                        //resultList.Items.Add(new ListViewItem(position.ToString(), newGroup));
                    }
                }

                watch.Stop();

                tbInfo.AppendText("Statistics\r\n");
                tbInfo.AppendText("\r\nTime:\t\t" + watch.ElapsedMilliseconds + " ms");
                tbInfo.AppendText("\r\nDocuments:\t" + _invertedIndex.DocumentCount.ToString());
                tbInfo.AppendText("\r\nTerms:\t\t" + _invertedIndex.TermCount.ToString());
                tbInfo.AppendText("\r\nAvg. Posting:\t" + _invertedIndex.AvgPostingListLength.ToString());

                tbInfo.AppendText("\r\n\r\nMost common terms:\t");
                foreach (var term in _invertedIndex.MostCommonTerms50)
                {
                    tbInfo.AppendText(term + ", ");
                }
                
            }
            catch (Exception)
            {
                //TODO: Maybe we should add something meaningful here.
                //Well, first make it work, then make it pretty, but we both know
                //that this means I will probably never fix this...
            }

        }
        #endregion
    }
}
